home *** CD-ROM | disk | FTP | other *** search
/ Experimental BBS Explossion 3 / Experimental BBS Explossion III.iso / gus / vts139b.zip / GUS.PAS < prev    next >
Pascal/Delphi Source File  |  1993-12-22  |  28KB  |  1,006 lines

  1. {****************************************************************************}
  2. {                                                                            }
  3. { MODULE:         GUS                                                        }
  4. {                                                                            }
  5. { DESCRIPTION:    This UNIT implements an interface to the GUS sound card.   }
  6. {                                                                            }
  7. { AUTHOR:         Juan Carlos Arévalo                                        }
  8. {                                                                            }
  9. { MODIFICATIONS:  Nobody (yet... ;-)                                         }
  10. {                                                                            }
  11. { HISTORY:        29-Aug-1993 Creation/definition.                           }
  12. {                                                                            }
  13. { (C) 1992,93 VangeliSTeam                                                   }
  14. {____________________________________________________________________________}
  15.  
  16. UNIT GUS;
  17.  
  18. INTERFACE
  19.  
  20. CONST
  21.   GUSPort     : WORD = $FFFF;
  22.   GUSIrq      : WORD = 11;
  23.   GUSChannels : BYTE = 32;
  24.   GUSDivisor  : WORD = 19293;
  25.  
  26.   grDRAMLowIO       = $43;
  27.   grDRAMHighIO      = $44;
  28.   grTimerControl    = $45;
  29.   grTimer1Count     = $46;
  30.   grTimer2Count     = $47;
  31.   grReset           = $4C;
  32.   grActiveVoices    = $0E;
  33.   grVoiceIRQ        = $0F;
  34.  
  35.   grVoiceControl    = $00;
  36.   grFreqControl     = $01;
  37.   grStartAddrHigh   = $02;
  38.   grStartAddrLow    = $03;
  39.   grEndAddrHigh     = $04;
  40.   grEndAddrLow      = $05;
  41.   grRampRate        = $06;
  42.   grRampStart       = $07;
  43.   grRampEnd         = $08;
  44.   grCurrentVol      = $09;
  45.   grCurrentAddrHigh = $0A;
  46.   grCurrentAddrLow  = $0B;
  47.   grPanPosition     = $0C;
  48.   grVolumeControl   = $0D;
  49.  
  50.   RampFastRate = $1F;
  51.  
  52.  
  53.  
  54.  
  55. PROCEDURE StartUltrasound;
  56. FUNCTION  DetectUltrasound : BOOLEAN;
  57. PROCEDURE DumpToUltrasound(VAR Src; Size: WORD; Dest: LONGINT; Signed: BOOLEAN);
  58. PROCEDURE ChangeVoiceParams(Channel: BYTE; Vol: BYTE; Freq: LONGINT; Panning: BYTE);
  59. PROCEDURE TriggerVoice(Channel: BYTE; Vol: BYTE; Freq: LONGINT; Panning: BYTE;
  60.                        VoiceStart, LoopStart, VoiceEnd: LONGINT);
  61. PROCEDURE SetGusChannels(Channels: BYTE);
  62.  
  63.  
  64. CONST
  65.   GUSTimer1RutPtr : POINTER = NIL;
  66.   GUSTimer2RutPtr : POINTER = NIL;
  67. VAR
  68.   GUSTimer1Rut    : PROCEDURE ABSOLUTE GUSTimer1RutPtr;
  69.   GUSTimer2Rut    : PROCEDURE ABSOLUTE GUSTimer2RutPtr;
  70.  
  71. PROCEDURE GUSInitTimer1(val: BYTE);
  72. PROCEDURE GUSInitTimer2(val: BYTE);
  73. PROCEDURE GUSStopTimer2;
  74. PROCEDURE GUSStopTimer1;
  75.  
  76. PROCEDURE InitGusIRQ;
  77. PROCEDURE DoneGusIRQ;
  78.  
  79.  
  80.  
  81.  
  82. IMPLEMENTATION
  83.  
  84. USES Hardware, Debugging;
  85.  
  86.  
  87.  
  88.  
  89. { Basic procedures }
  90.  
  91. PROCEDURE SetGusVoice (Voice: BYTE); ASSEMBLER;
  92.   ASM
  93.                 MOV     DX,[GUSPort]
  94.                 ADD     DX,102h
  95.                 MOV     AL,[Voice]
  96.                 OUT     DX,AL
  97.   END;
  98.  
  99. FUNCTION GetGusRegister8 (Reg : BYTE) : BYTE; ASSEMBLER;
  100.   ASM
  101.                 MOV     DX,[GUSPort]
  102.                 ADD     DX,103h
  103.                 MOV     AL,[Reg]
  104.                 CMP     AL,$40
  105.                 JNC     @@c1
  106.                  ADD    AL,80h
  107.         @@c1:   OUT     DX,AL
  108.                 ADD     DX,2
  109.                 IN      AL,DX
  110.   END;
  111.  
  112. PROCEDURE SetGusRegister8 (Reg, Val : BYTE); ASSEMBLER;
  113.   ASM
  114.                 MOV     DX,[GUSPort]
  115.                 ADD     DX,103h
  116.                 MOV     AL,[Reg]
  117.                 OUT     DX,AL
  118.                 ADD     DX,2
  119.                 MOV     AL,[Val]
  120.                 OUT     DX,AL
  121.   END;
  122.  
  123. FUNCTION GetGusRegister16 (Reg : BYTE) : WORD; ASSEMBLER;
  124.   ASM
  125.                 MOV     DX,[GUSPort]
  126.                 ADD     DX,103h
  127.                 MOV     AL,[Reg]
  128.                 CMP     AL,$40
  129.                 JNC     @@c1
  130.                  ADD    AL,80h
  131.         @@c1:   OUT     DX,AL
  132.                 INC     DX
  133.                 IN      AX,DX
  134.   END;
  135.  
  136. PROCEDURE SetGusRegister16 (Reg: BYTE; Val : WORD); ASSEMBLER;
  137.   ASM
  138.                 MOV     DX,[GUSPort]
  139.                 ADD     DX,103h
  140.                 MOV     AL,[Reg]
  141.                 OUT     DX,AL
  142.                 INC     DX
  143.                 MOV     AX,[Val]
  144.                 OUT     DX,AX
  145.   END;
  146.  
  147. PROCEDURE GusDelay; ASSEMBLER;
  148.   ASM
  149.                 MOV     DX,[GUSPort]
  150.                 IN      AL,DX
  151.                 IN      AL,DX
  152.                 IN      AL,DX
  153.                 IN      AL,DX
  154.                 IN      AL,DX
  155.                 IN      AL,DX
  156.                 IN      AL,DX
  157.   END;
  158.  
  159.  
  160.  
  161.  
  162.  
  163.  
  164.  
  165.  
  166.  
  167. PROCEDURE DumpToUltrasound(VAR Src; Size: WORD; Dest: LONGINT; Signed: BOOLEAN);
  168.   BEGIN
  169.     SetGusRegister8 (grDRAMHighIO, Dest SHR 16);
  170.     SetGusRegister16(grDRAMLowIO,  Dest AND $FFFF);
  171.  
  172.     ASM
  173.                 PUSH    DS
  174.                 MOV     CX,[Size]
  175.                 LES     DI,[Dest]
  176.                 MOV     BX,ES
  177.                 MOV     BH,[Signed]
  178.                 CMP     BH,1
  179.                 SBB     BH,BH
  180.                 SHL     BH,7
  181.                 MOV     DX,[GUSPort]
  182.                 LDS     SI,[Src]
  183.                 ADD     DX,103h
  184.         @@loop:
  185.                  MOV    AL,grDRAMLowIO
  186.                  OUT    DX,AL
  187.                  INC    DX
  188.                  MOV    AX,DI
  189.                  OUT    DX,AX
  190.  
  191.                  ADD    DX,3
  192.                  LODSB
  193.                  XOR    AL,BH
  194.                  OUT    DX,AL
  195.                  INC    DI
  196.                  JNZ    @@c1
  197.                   INC   BL
  198.                   SUB   DX,4
  199.                   MOV   AL,grDRAMHighIO
  200.                   OUT   DX,AL
  201.                   ADD   DX,2
  202.                   MOV   AL,BL
  203.                   OUT   DX,AL
  204.                   ADD   DX,2
  205.         @@c1:
  206.                  SUB    DX,4
  207.                  LOOP   @@loop
  208.  
  209.                 POP     DS
  210.     END;
  211.  
  212.   END;
  213.  
  214.  
  215.  
  216.  
  217.  
  218.  
  219.  
  220.  
  221.  
  222.  
  223.  
  224.  
  225.  
  226.  
  227.  
  228.  
  229. CONST
  230.   TimerControl : BYTE = 0;
  231.   TimerMask    : BYTE = 0;
  232.  
  233. PROCEDURE GUSInitTimer1(val: BYTE);
  234.   BEGIN
  235.     SetGusRegister8(grTimer1Count,  val);
  236.     TimerControl := TimerControl OR 4;
  237.     SetGusRegister8(grTimerControl, TimerControl);
  238.     TimerMask := TimerMask OR 1;
  239.     Port[GUSPort+8] := 4;
  240.     Port[GUSPort+9] := TimerMask;
  241.   END;
  242.  
  243.  
  244. PROCEDURE GUSInitTimer2(val: BYTE);
  245.   BEGIN
  246.     SetGusRegister8(grTimer2Count,  val);
  247.     TimerControl := TimerControl OR 8;
  248.     SetGusRegister8(grTimerControl, TimerControl);
  249.     TimerMask := TimerMask OR 2;
  250.     Port[GUSPort+8] := 4;
  251.     Port[GUSPort+9] := TimerMask;
  252.   END;
  253.  
  254.  
  255. PROCEDURE GUSStopTimer1;
  256.   BEGIN
  257.     TimerControl := TimerControl AND NOT 4;
  258.     SetGusRegister8(grTimerControl, TimerControl);
  259.     TimerMask := TimerMask AND NOT 1;
  260.     Port[GUSPort+8] := 4;
  261.     Port[GUSPort+9] := TimerMask;
  262.   END;
  263.  
  264.  
  265. PROCEDURE GUSStopTimer2;
  266.   BEGIN
  267.     TimerControl := TimerControl AND NOT 8;
  268.     SetGusRegister8(grTimerControl, TimerControl);
  269.     TimerMask := TimerMask AND NOT 2;
  270.     Port[GUSPort+8] := 4;
  271.     Port[GUSPort+9] := TimerMask;
  272.   END;
  273.  
  274.  
  275.  
  276.  
  277.  
  278.  
  279. PROCEDURE ServiceTimer1;
  280.   BEGIN
  281.     SetGusRegister8(grTimerControl, TimerControl AND NOT 4);
  282.     SetGusRegister8(grTimerControl, TimerControl);
  283.     IF GUSTimer1RutPtr <> NIL THEN
  284.       GUSTimer1Rut;
  285.   END;
  286.  
  287.  
  288. PROCEDURE ServiceTimer2;
  289.   BEGIN
  290.     SetGusRegister8(grTimerControl, TimerControl AND NOT 8);
  291.     SetGusRegister8(grTimerControl, TimerControl);
  292.     IF GUSTimer2RutPtr <> NIL THEN
  293.       GUSTimer2Rut;
  294.   END;
  295.  
  296.  
  297. PROCEDURE GUSIrqHandler; ASSEMBLER;
  298.   ASM
  299.  
  300.                 CLI
  301.  
  302.                 PUSHA
  303.                 PUSH    ES
  304.                 PUSH    DS
  305. {
  306.                 MOV     AX,$B800
  307.                 MOV     DS,AX
  308.                 INC     [WORD PTR 10]
  309. }
  310.                 MOV     AX,SEG @Data
  311.                 MOV     DS,AX
  312.  
  313.                 MOV     AL,$20
  314.                 OUT     $20,AL
  315.                 MOV     AH,[BYTE PTR GUSIrq]
  316.                 CMP     AH,8
  317.                 JC      @@c1
  318.                  OUT    $A0,AL
  319.         @@c1:   MOV     AL,AH
  320.                 XOR     AH,AH
  321.                 PUSH    AX
  322.                 CALL    DisableIRQ
  323.  
  324.         @@loop: CLI
  325.                 MOV     DX,[GUSPort]
  326.                 ADD     DX,6
  327.                 IN      AL,DX
  328.  
  329.                 TEST    AL,00001100b
  330.                 JZ      @@fin
  331.  
  332.                 STI
  333.  
  334.                 TEST    AL,00000100b
  335.                 JZ      @@no1
  336.                  PUSH   AX
  337.                  CALL   ServiceTimer1
  338.                  POP    AX
  339.         @@no1:  TEST    AL,00001000b
  340.                 JZ      @@no2
  341.                  PUSH   AX
  342.                  CALL   ServiceTimer2
  343.                  POP    AX
  344.         @@no2:  JMP     @@loop
  345.  
  346.         @@fin:  MOV     AL,[BYTE PTR GUSIrq]
  347.                 XOR     AH,AH
  348.                 PUSH    AX
  349.                 CALL    EnableIRQ
  350.  
  351.                 PUSH    grVoiceIRQ
  352.                 CALL    GetGusRegister8
  353.  
  354.                 POP     DS
  355.                 POP     ES
  356.                 POPA
  357.                 IRET
  358.  
  359.   END;
  360.  
  361.  
  362. CONST
  363.  
  364.   OldIrqHandler : POINTER = NIL;
  365.   OldIrqState   : BOOLEAN = FALSE;
  366. {
  367.   i : WORD = 0;
  368.   OldIrqHandler : ARRAY[2..15] OF POINTER = ( NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL );
  369.   OldIrqState   : ARRAY[2..15] OF BOOLEAN = ( FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,
  370.                                               FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE);
  371. }
  372.   GusIRQInited  : BOOLEAN = FALSE;
  373.  
  374. PROCEDURE InitGusIRQ;
  375.   BEGIN
  376.     IF GusIRQInited THEN EXIT;
  377.     GusIRQInited := TRUE;
  378. {
  379.     FOR i := 2 TO 15 DO
  380.       BEGIN
  381.         OldIrqState[i]   := IRQState(i);
  382.         OldIrqHandler[i] := SetIrqVector(i, @GUSIrqHandler);
  383.         EnableIRQ(i);
  384.       END;
  385. }
  386.     OldIrqState   := IRQState(GUSIrq);
  387.     OldIrqHandler := SetIrqVector(GUSIrq, @GUSIrqHandler);
  388.     EnableIRQ(GUSIrq);
  389.     GetGusRegister8(grVoiceIRQ);
  390.  
  391.   END;
  392.  
  393. PROCEDURE DoneGusIRQ;
  394.   BEGIN
  395.     IF NOT GusIRQInited THEN EXIT;
  396.     GusIRQInited := FALSE;
  397. {
  398.     FOR i := 2 TO 15 DO
  399.       BEGIN
  400.         IF NOT OldIrqState[i] THEN
  401.           DisableIRQ(GUSIrq);
  402.         SetIrqVector(GUSIrq, OldIrqHandler[i]);
  403.       END;
  404. }
  405.     IF NOT OldIrqState THEN
  406.       DisableIRQ(GUSIrq);
  407.     SetIrqVector(GUSIrq, OldIrqHandler);
  408.  
  409.   END;
  410.  
  411.  
  412.  
  413.  
  414.  
  415.  
  416.  
  417.  
  418.  
  419.  
  420.  
  421.  
  422.  
  423.  
  424.  
  425.  
  426.  
  427.  
  428.  
  429.  
  430. VAR
  431.   LastVoiceStart : ARRAY[0..31] OF LONGINT;
  432.   LastLoopStart  : ARRAY[0..31] OF LONGINT;
  433.   LastVoiceEnd   : ARRAY[0..31] OF LONGINT;
  434.   LastVolumes    : ARRAY[0..31] OF BYTE;
  435.   LastFreqs      : ARRAY[0..31] OF LONGINT;
  436.   LastPans       : ARRAY[0..31] OF BYTE;
  437.   VoicesChanged  : ARRAY[0..31] OF BOOLEAN;
  438.  
  439.  
  440.  
  441.  
  442.  
  443.  
  444. PROCEDURE StartUltrasound;
  445.   VAR
  446.     i : WORD;
  447.   BEGIN
  448.     FillChar(LastVoiceStart, SizeOf(LastVoiceStart), $FF);
  449.     FillChar(LastLoopStart,  SizeOf(LastLoopStart),  $FE);
  450.     FillChar(LastVoiceEnd,   SizeOf(LastVoiceEnd),   $FF);
  451.     FillChar(LastVolumes,    SizeOf(LastVolumes),    $FF);
  452.     FillChar(LastFreqs,      SizeOf(LastFreqs),      $FF);
  453.     FillChar(LastPans,       SizeOf(LastPans),       $7F);
  454.     FillChar(VoicesChanged,  SizeOf(VoicesChanged),  0);
  455.  
  456.     ASM
  457.                 MOV     DX,[GUSPort]
  458.                 MOV     AL,00001011b    ; { Turn off output, to avoid clicks. }
  459.                 OUT     DX,AL
  460.     END;
  461.  
  462.     SetGusRegister8  (grReset, 0);          ; { Reset the GUS card. }
  463.     GusDelay;
  464.     SetGusRegister8  (grReset, 1);          ; { DAC turned off. }
  465.  
  466.     SetGusRegister8  (grActiveVoices, $C0+31);  ; { 32 voices }
  467.     SetGusRegister16 (grDRAMLowIO, 0);
  468.     SetGusRegister8  (grDRAMHighIO, 0);
  469.  
  470.     FOR i := 0 TO 31 DO
  471.       BEGIN
  472.         SetGusVoice(i);
  473.  
  474.         SetGusRegister8  (grVoiceControl , 3);     ; { Voice stopped }
  475.         SetGusRegister8  (grVolumeControl, 3);
  476.         IF GetGusRegister16(grCurrentVol) > $1800 THEN
  477.           BEGIN
  478.             SetGusRegister8  (grRampStart    , $18);
  479.             SetGusRegister8  (grRampEnd      , HI(GetGusRegister16(grCurrentVol)));
  480.             SetGusRegister8  (grRampRate     , RampFastRate);
  481.             SetGusRegister8  (grVolumeControl, $40);   ; { Decreasing Ramp }
  482.           END;
  483.       END;
  484.  
  485.     SetGusRegister8  (grReset, 7);          ; { DAC turned on. }
  486.  
  487.     ASM
  488.                 MOV     DX,[GUSPort]
  489.                 MOV     AL,00001100b    ; { Turn on output }
  490.                 OUT     DX,AL
  491.     END;
  492.  
  493.     SetGusChannels(GUSChannels);
  494.   END;
  495.  
  496.  
  497.  
  498.  
  499. FUNCTION ProbeUltrasound : BOOLEAN;
  500.   VAR
  501.     v0, v1 : WORD;
  502.   LABEL
  503.     Fin;
  504.   BEGIN
  505.     ProbeUltrasound := FALSE;
  506.  
  507.     ASM CLI END;
  508.  
  509.     SetGusRegister8  (grReset, 0);
  510.     GusDelay;
  511.     SetGusRegister8  (grReset, 7);
  512.     SetGusRegister8  (grActiveVoices, $C0+31);
  513.  
  514.     SetGusVoice(0);
  515.     v0 := GetGusRegister16(grStartAddrHigh);
  516.     SetGusRegister16(grStartAddrHigh, $16D8);
  517.     SetGusVoice(1);
  518.     v1 := GetGusRegister16(grStartAddrHigh);
  519.     SetGusRegister16(grStartAddrHigh, $0F83);
  520.  
  521.     SetGusVoice(0);
  522.     IF (GetGusRegister16(grStartAddrHigh) AND $1FFF) <> $16D8 THEN GOTO Fin;
  523.     SetGusVoice(1);
  524.     IF (GetGusRegister16(grStartAddrHigh) AND $1FFF) <> $0F83 THEN GOTO Fin;
  525.  
  526.     ProbeUltrasound := TRUE;
  527. Fin:
  528.     SetGusVoice(0);
  529.     SetGusRegister16(grStartAddrHigh, v0);
  530.     SetGusVoice(1);
  531.     SetGusRegister16(grStartAddrHigh, v1);
  532.  
  533.     ASM STI END;
  534.  
  535.   END;
  536.  
  537.  
  538.  
  539.  
  540. FUNCTION DetectUltrasound : BOOLEAN;
  541.   CONST
  542.     GUSPorts : ARRAY[1..12] OF WORD = ( $220, $240, $200, $210, $230, $250,
  543.                                         $260, $270, $280, $290, $2A0, $2B0 );
  544.   VAR
  545.     i : WORD;
  546.   BEGIN
  547.     DetectUltrasound := TRUE;
  548.  
  549.     IF GUSPort = $FFFF THEN
  550.       FOR i := 1 TO 12 DO
  551.         BEGIN
  552.           GUSPort := GUSPorts[i];
  553.           IF ProbeUltrasound THEN EXIT;
  554.         END
  555.     ELSE
  556.       IF ProbeUltrasound THEN EXIT;
  557.  
  558.     DetectUltrasound := FALSE;
  559.  
  560.   END;
  561.  
  562. (*
  563.         SetGusRegister8  (grVoiceControl   , 3);
  564.         SetGusRegister16 (grFreqControl    , 0);
  565.         SetGusRegister16 (grStartAddrLow   , 0);
  566.         SetGusRegister16 (grStartAddrHigh  , 0);
  567.         SetGusRegister16 (grEndAddrLow     , 0);
  568.         SetGusRegister16 (grEndAddrHigh    , 0);
  569.         SetGusRegister8  (grRampStart      , 0);
  570.         SetGusRegister8  (grRampEnd        , 0);
  571.         SetGusRegister16 (grCurrentVol     , 0);
  572.         SetGusRegister8  (grRampRate       , 00111111b);
  573.         SetGusRegister16 (grCurrentAddrLow , 0);
  574.         SetGusRegister16 (grCurrentAddrHigh, 0);
  575.         SetGusRegister8  (grPanPosition    , 7);
  576.         SetGusRegister8  (grVolumeControl  , 00000000b);
  577. *)
  578.  
  579.  
  580.  
  581.  
  582.  
  583.  
  584.  
  585. PROCEDURE SwapGusAddress(VAR Addr: LONGINT);
  586.   BEGIN
  587.     Addr := (Addr SHL 9) AND $1FFFFE00;
  588.   END;
  589.  
  590. PROCEDURE ChangeVol(Channel, Vol, Ramp: BYTE);
  591.   CONST
  592.     VolumeTable : ARRAY[0..127] OF WORD =
  593.       (
  594. 6144,              
  595. { $01000 } {a$8FF0,}{ $01DFB } $9DF0,  { $02BF7 } $A5F0,  { $039F3 } $ACF0,
  596. { $047EF } $B1F0,  { $055EB } $B570,  { $063E7 } $B8F0,  { $071E3 } $BC70,  
  597. { $07FDF } $BFF0,  { $08DDB } $C1B0,  { $09BD7 } $C370,  { $0A9D3 } $C530,  
  598. { $0B7CF } $C6F0,  { $0C5CB } $C8B0,  { $0D3C7 } $CA70,  { $0E1C3 } $CC30,  
  599. { $0EFBF } $CDF0,  { $0FDBB } $CFB0,  { $10BB7 } $D0B0,  { $119B3 } $D190,  
  600. { $127AF } $D270,  { $135AB } $D350,  { $143A7 } $D430,  { $151A3 } $D510,  
  601. { $15F9F } $D5F0,  { $16D9B } $D6D0,  { $17B97 } $D7B0,  { $18993 } $D890,  
  602. { $1978F } $D970,  { $1A58B } $DA50,  { $1B387 } $DB30,  { $1C183 } $DC10,  
  603. { $1CF7E } $DCF0,  { $1DD7A } $DDD0,  { $1EB76 } $DEB0,  { $1F972 } $DF90,  
  604. { $2076E } $E030,  { $2156A } $E0A0,  { $22366 } $E110,  { $23162 } $E180,  
  605. { $23F5E } $E1F0,  { $24D5A } $E260,  { $25B56 } $E2D0,  { $26952 } $E340,  
  606. { $2774E } $E3B0,  { $2854A } $E420,  { $29346 } $E490,  { $2A142 } $E500,  
  607. { $2AF3E } $E570,  { $2BD3A } $E5E0,  { $2CB36 } $E650,  { $2D932 } $E6C0,  
  608. { $2E72E } $E730,  { $2F52A } $E7A0,  { $30326 } $E810,  { $31122 } $E880,  
  609. { $31F1E } $E8F0,  { $32D1A } $E960,  { $33B16 } $E9D0,  { $34912 } $EA40,  
  610. { $3570E } $EAB0,  { $3650A } $EB20,  { $37306 } $EB90,  { $38102 } $EC00,  
  611. { $38EFD } $EC70,  { $39CF9 } $ECE0,  { $3AAF5 } $ED50,  { $3B8F1 } $EDC0,  
  612. { $3C6ED } $EE30,  { $3D4E9 } $EEA0,  { $3E2E5 } $EF10,  { $3F0E1 } $EF80,  
  613. { $3FEDD } $EFF0,  { $40CD9 } $F030,  { $41AD5 } $F060,  { $428D1 } $F0A0,  
  614. { $436CD } $F0D0,  { $444C9 } $F110,  { $452C5 } $F140,  { $460C1 } $F180,  
  615. { $46EBD } $F1B0,  { $47CB9 } $F1F0,  { $48AB5 } $F220,  { $498B1 } $F260,  
  616. { $4A6AD } $F290,  { $4B4A9 } $F2D0,  { $4C2A5 } $F300,  { $4D0A1 } $F340,  
  617. { $4DE9D } $F370,  { $4EC99 } $F3B0,  { $4FA95 } $F3E0,  { $50891 } $F420,  
  618. { $5168D } $F450,  { $52489 } $F490,  { $53285 } $F4C0,  { $54081 } $F500,  
  619. { $54E7C } $F530,  { $55C78 } $F570,  { $56A74 } $F5A0,  { $57870 } $F5E0,  
  620. { $5866C } $F610,  { $59468 } $F650,  { $5A264 } $F680,  { $5B060 } $F6C0,  
  621. { $5BE5C } $F6F0,  { $5CC58 } $F730,  { $5DA54 } $F760,  { $5E850 } $F7A0,  
  622. { $5F64C } $F7D0,  { $60448 } $F810,  { $61244 } $F840,  { $62040 } $F880,  
  623. { $62E3C } $F8B0,  { $63C38 } $F8F0,  { $64A34 } $F920,  { $65830 } $F960,  
  624. { $6662C } $F990,  { $67428 } $F9D0,  { $68224 } $FA00,  { $69020 } $FA40,  
  625. { $69E1C } $FA70,  { $6AC18 } $FAB0,  { $6BA14 } $FAE0,  { $6C810 } $FB20,  
  626. { $6D60C } $FB50,  { $6E408 } $FB90,  { $6F204 } $FBC0,  { $70000 } $FC00
  627.  
  628.       );
  629.   VAR
  630.     Oldv : BYTE;
  631.   BEGIN
  632.     IF (Vol > 127) AND (Vol <> $FF) THEN Vol := 127;
  633.  
  634.     IF (Vol <> $FF) AND (LastVolumes[Channel] <> Vol) THEN
  635.       BEGIN
  636.         LastVolumes[Channel] := Vol;
  637.         IF Channel = 2 THEN
  638.           BEGIN
  639.             WriteSNum(Channel, $4F);
  640.             WriteSNum(Vol, $1F);
  641.           END;
  642.         SetGusRegister8(grVolumeControl, 3);
  643.         Oldv := HI(GetGusRegister16(grCurrentVol));
  644.         IF Oldv <> HI(VolumeTable[Vol]) THEN
  645.           BEGIN
  646.             IF Oldv > HI(VolumeTable[Vol]) THEN
  647.               BEGIN
  648.                 SetGusRegister8(grRampEnd      , Oldv);
  649.                 SetGusRegister8(grRampStart    , HI(VolumeTable[Vol]));
  650.               END
  651.             ELSE
  652.               BEGIN
  653.                 SetGusRegister8(grRampStart    , Oldv);
  654.                 SetGusRegister8(grRampEnd      , HI(VolumeTable[Vol]));
  655.               END;
  656.  
  657.             SetGusRegister16(grCurrentVol  , WORD(Oldv) SHL 8);
  658.             SetGusRegister8(grRampRate     , Ramp);
  659.  
  660.             IF Oldv > HI(VolumeTable[Vol]) THEN
  661.               SetGusRegister8(grVolumeControl, GetGusRegister8(grVolumeControl) AND (NOT 3) OR $40)
  662.             ELSE
  663.               SetGusRegister8(grVolumeControl, GetGusRegister8(grVolumeControl) AND (NOT 3) AND NOT $40);
  664.           END;
  665.  
  666.       END;
  667.  
  668.   END;
  669.  
  670. PROCEDURE ChangeSample(Channel: BYTE);
  671.   VAR
  672.     VoiceStart,
  673.     LoopStart,
  674.     VoiceEnd: LONGINT;
  675.   BEGIN
  676.     VoiceStart := LastVoiceStart[Channel];
  677.     LoopStart  := LastLoopStart[Channel];
  678.     VoiceEnd   := LastVoiceEnd[Channel];
  679.  
  680.     SetGusRegister8(grVoiceControl, GetGusRegister8(grVoiceControl) OR 3);
  681.     SwapGusAddress(VoiceStart);
  682.     SetGusRegister16 (grCurrentAddrLow , VoiceStart AND $FFFF);
  683.     SetGusRegister16 (grCurrentAddrHigh, VoiceStart SHR 16);
  684.  
  685.     SwapGusAddress(VoiceEnd);
  686.     SetGusRegister16 (grEndAddrLow     , VoiceEnd   AND $FFFF);
  687.     SetGusRegister16 (grEndAddrHigh    , VoiceEnd   SHR 16);
  688.  
  689.     IF LoopStart = -1 THEN
  690.       SetGusRegister8(grVoiceControl, GetGusRegister8(grVoiceControl) AND NOT 8)
  691.     ELSE
  692.       BEGIN
  693.         SetGusRegister8(grVoiceControl, GetGusRegister8(grVoiceControl) OR 8);
  694.         SwapGusAddress(LoopStart);
  695.         SetGusRegister16 (grStartAddrLow   , LoopStart  AND $FFFF);
  696.         SetGusRegister16 (grStartAddrHigh  , LoopStart  SHR 16);
  697.       END;
  698.   END;
  699.  
  700. PROCEDURE RestartChannels; FAR;
  701.   CONST
  702.     buf    : ARRAY[1..64] OF BYTE = (0, 0, 0, 0, 0, 0, 0, 0,
  703.                                      0, 0, 0, 0, 0, 0, 0, 0,
  704.                                      0, 0, 0, 0, 0, 0, 0, 0,
  705.                                      0, 0, 0, 0, 0, 0, 0, 0,
  706.                                      0, 0, 0, 0, 0, 0, 0, 0,
  707.                                      0, 0, 0, 0, 0, 0, 0, 0,
  708.                                      0, 0, 0, 0, 0, 0, 0, 0,
  709.                                      0, 0, 0, 0, 0, 0, 0, 0);
  710.   VAR
  711.     i, 
  712.     k, Vol : BYTE;
  713.     j      : WORD;
  714.     Scr    : WORD ABSOLUTE $B800:90*63*2;
  715.     First  : BOOLEAN;
  716.   BEGIN
  717.     GUSStopTimer1;
  718. {    INC(Scr);}
  719.     FOR i := 0 TO GUSChannels-1 DO
  720.       IF VoicesChanged[i] THEN
  721.         BEGIN
  722.           SetGusVoice(i);
  723.           ChangeSample(i);
  724.           Vol := LastVolumes[i];
  725.           LastVolumes[i] := 0;
  726.           ChangeVol(i, Vol, RampFastRate);
  727.         END;
  728.  
  729.  
  730.     j := 0;
  731.     FOR i := 0 TO GUSChannels-1 DO
  732.       IF VoicesChanged[i] THEN
  733.         BEGIN
  734.           INC(j, 2);
  735.           buf[j-1] := i;
  736.           buf[j]   := GetGusRegister8(grVoiceControl) AND NOT 3;
  737. {          VoicesChanged[i] := FALSE;}
  738.         END;
  739. (*
  740.     IF j > 0 THEN
  741.       BEGIN
  742.         i := buf[j-1];
  743.         k := buf[j];
  744.  
  745.         ASM
  746.                 MOV     DX,[GUSPort]
  747.                 ADD     DX,102h
  748.                 MOV     AL,[i]
  749.                 OUT     DX,AL
  750.                 INC     DX
  751.  
  752.                 MOV     AL,grVoiceControl+$80
  753.                 OUT     DX,AL
  754.                 ADD     DX,2
  755.                 IN      AL,DX
  756.                 MOV     BH,AL
  757.                 SUB     DX,2
  758.  
  759.                 MOV     AL,grCurrentAddrLow+$80
  760.                 OUT     DX,AL
  761.                 INC     DX
  762.                 IN      AX,DX
  763.                 MOV     SI,AX
  764.                 DEC     DX
  765.  
  766.                 MOV     AL,grCurrentAddrHigh+$80
  767.                 OUT     DX,AL
  768.                 INC     DX
  769.                 IN      AX,DX
  770.                 MOV     DI,AX
  771.                 DEC     DX
  772.  
  773.                 MOV     AL,grVoiceControl
  774.                 OUT     DX,AL
  775.                 ADD     DX,2
  776.                 MOV     AL,[k]
  777.                 OUT     DX,AL
  778.                 SUB     DX,2
  779.  
  780.                 MOV     CX,000
  781.         @@lp:    MOV    AL,grCurrentAddrLow+$80
  782.                  OUT    DX,AL
  783.                  INC    DX
  784.                  IN     AX,DX
  785.                  DEC    DX
  786.                  CMP    AX,SI
  787.                  JNZ    @@nof
  788.                  LOOP   @@lp
  789.         @@nof:  
  790.                 MOV     AL,grVoiceControl
  791.                 OUT     DX,AL
  792.                 ADD     DX,2
  793.                 MOV     AL,BH
  794.                 OUT     DX,AL
  795.                 SUB     DX,2
  796.  
  797.                 MOV     AL,grCurrentAddrLow
  798.                 OUT     DX,AL
  799.                 MOV     AX,SI
  800.                 INC     DX
  801.                 OUT     DX,AX
  802.                 DEC     DX
  803.  
  804.                 MOV     AL,grCurrentAddrHigh
  805.                 OUT     DX,AL
  806.                 MOV     AX,DI
  807.                 INC     DX
  808.                 OUT     DX,AX
  809.  
  810.  
  811.                 MOV     CX,[j]
  812.                 SHR     CX,1
  813.                 MOV     SI,OFFSET buf
  814.         @@lp2:   MOV    AL,[SI]
  815.                  INC    SI
  816.  
  817.                  MOV    DX,[GUSPort]
  818.                  ADD    DX,102h
  819.                  OUT    DX,AL
  820.  
  821.                  INC    DX
  822.                  MOV    AL,grVoiceControl
  823.                  OUT    DX,AL
  824.                  ADD    DX,2
  825.                  MOV    AL,[SI]
  826.                  INC    SI
  827.                  OUT    DX,AL
  828.  
  829.                  LOOP   @@lp2
  830.  
  831.  
  832.  
  833.  
  834. {
  835.                 MOV     AL,[First]
  836.                 JZ      @@nof
  837.                  XOR    AL,AL
  838.                  MOV    [First],AL
  839.                  SUB    DX,2
  840.                  MOV    AL,grCurrentAddrLow+$80
  841.                  OUT    DX,AL
  842.                  INC    DX
  843.                  IN     AX,DX
  844.                  MOV    SI,AX
  845.                  MOV    CX,10000
  846.         @@lp:     IN    AX,DX
  847.                   CMP   AX,SI
  848.                   JNZ   @@nof
  849.                   LOOP  @@lp
  850.         @@nof:
  851. }
  852.  
  853.  
  854.  
  855.  
  856.         END;
  857.     END;
  858. *)
  859.  
  860.  
  861.     First := TRUE;
  862.     FOR i := 0 TO GUSChannels-1 DO
  863.       IF VoicesChanged[i] THEN
  864.         BEGIN
  865. (*
  866.           ASM
  867.  
  868.                 MOV     DX,[GUSPort]
  869.                 ADD     DX,102h
  870.                 MOV     AL,[i]
  871.                 OUT     DX,AL
  872.  
  873.                 INC     DX
  874.                 MOV     AL,grVoiceControl+$80
  875.                 OUT     DX,AL
  876.                 ADD     DX,2
  877.                 IN      AL,DX
  878.                 MOV     AH,AL
  879.  
  880.                 SUB     DX,2
  881.                 MOV     AL,grVoiceControl
  882.                 OUT     DX,AL
  883.                 ADD     DX,2
  884.                 MOV     AL,AH
  885.                 AND     AL,NOT 3
  886.                 OUT     DX,AL
  887. {
  888.                 MOV     AL,[First]
  889.                 JZ      @@nof
  890.                  XOR    AL,AL
  891.                  MOV    [First],AL
  892.                  SUB    DX,2
  893.                  MOV    AL,grCurrentAddrLow+$80
  894.                  OUT    DX,AL
  895.                  INC    DX
  896.                  IN     AX,DX
  897.                  MOV    SI,AX
  898.                  MOV    CX,10000
  899.         @@lp:     IN    AX,DX
  900.                   CMP   AX,SI
  901.                   JNZ   @@nof
  902.                   LOOP  @@lp
  903.         @@nof:
  904. }
  905.           END;
  906. *)
  907.  
  908.           SetGusVoice(i);
  909.           SetGusRegister8(grVoiceControl, GetGusRegister8(grVoiceControl) AND NOT 3);
  910.  
  911.           VoicesChanged[i] := FALSE;
  912.         END;
  913.  
  914.  
  915.   END;
  916.  
  917.  
  918. PROCEDURE TriggerVoice(Channel: BYTE; Vol: BYTE; Freq: LONGINT; Panning: BYTE;
  919.                        VoiceStart, LoopStart, VoiceEnd: LONGINT);
  920.   BEGIN
  921.     SetGusVoice(Channel);
  922.  
  923.     ChangeVoiceParams(Channel, 0, Freq, Panning);
  924.  
  925.     ASM PUSHF; CLI END;
  926.  
  927.     IF Channel = 2 THEN
  928.       BEGIN
  929.         WriteSNum(VoiceStart SHR 16,    $3F);
  930.         WriteSNum(VoiceStart AND $FFFF, $3F);
  931.  
  932.         WriteSNum(LoopStart SHR 16,    $5F);
  933.         WriteSNum(LoopStart AND $FFFF, $5F);
  934.  
  935.         WriteSNum(VoiceEnd SHR 16,    $F0);
  936.         WriteSNum(VoiceEnd AND $FFFF, $F0);
  937.       END;
  938.  
  939.     LastVoiceStart[Channel] := VoiceStart;
  940.     LastLoopStart[Channel]  := LoopStart;
  941.     LastVoiceEnd[Channel]   := VoiceEnd;
  942.     VoicesChanged[Channel]  := TRUE;
  943.     LastVolumes[Channel]    := Vol;
  944.  
  945.     ASM POPF END;
  946.  
  947.     GUSTimer1Rut := RestartChannels;
  948.     GUSInitTimer1($F6);
  949.   END;
  950.  
  951. PROCEDURE ChangeVoiceParams(Channel: BYTE; Vol: BYTE; Freq: LONGINT; Panning: BYTE);
  952.   BEGIN
  953.     SetGusVoice(Channel);
  954. {
  955. IF Vol = 0 THEN
  956.   Vol := 127
  957. ELSE IF Vol <> $FF THEN
  958.   Vol := 0;
  959. }
  960.  
  961.     IF VoicesChanged[Channel] THEN
  962.       BEGIN
  963.         IF Vol <> $FF THEN
  964.           BEGIN
  965.             IF Vol > 127 THEN Vol := 127;
  966.             LastVolumes[Channel] := Vol;
  967.           END;
  968.       END
  969.     ELSE
  970.       ChangeVol(Channel, Vol, RampFastRate);
  971.  
  972.     IF (Freq <> $FFFFFFFF) AND (LastFreqs[Channel] <> Freq) THEN
  973.       BEGIN
  974.         LastFreqs[Channel] := Freq;
  975.         SetGusRegister16(grFreqControl, ((LONGINT(Freq) SHL 9) + (GUSDivisor SHR 1)) DIV
  976.                                         (GUSDivisor SHR 1));
  977.       END;
  978.  
  979.     IF LastPans[Channel] <> Panning THEN
  980.       BEGIN
  981.         LastPans[Channel] := Panning;
  982.         SetGusRegister8(grPanPosition, Panning SHR 4);
  983.       END;
  984.  
  985.   END;
  986.  
  987.  
  988.  
  989. PROCEDURE SetGusChannels(Channels: BYTE);
  990.   BEGIN
  991.     IF Channels > 32 THEN
  992.       Channels := 32
  993.     ELSE IF Channels < 20 THEN
  994.       Channels := 20;
  995.  
  996.     SetGusRegister8  (grActiveVoices, $C0+Channels-1);
  997.  
  998.     GUSDivisor  := 617400 DIV Channels;
  999.     GUSChannels := Channels;
  1000.   END;
  1001.  
  1002.  
  1003.  
  1004.  
  1005. END.
  1006.